package com.example.demo.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;

import com.example.demo.utils.AllAlgorithm;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import javafx.util.Pair;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Atanycosts
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2023-03-06 15:52:59
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {


    /**
     * 匹配的用户
     * @param size
     * @param userId
     * @return
     */
    @Override
    public List<User> matchUsers(int size, Long userId){

        //查到对应user的Tags
        User byId = this.getById(userId);

        //查所有的
        QueryWrapper<User> userWrapper = new QueryWrapper<>();
        userWrapper.select("id","user_Tags");
        userWrapper.isNotNull("user_Tags");

        //将所有的用户转成List集合
        List<User> userList = this.list(userWrapper);

        //得到对应用户的标签
        String userTags = byId.getUserTags();

        //转成JSON
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(userTags, new TypeToken<List<String>>() {
        }.getType());

        // 用户列表的下标 => 相似度
        List<Pair<User, Double>> list = new ArrayList<>();
        // 依次计算所有用户和当前用户的相似度
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            String tags = user.getUserTags();
            // 无标签或者为当前用户自己
            if (StringUtils.isBlank(tags) || user.getId() == byId.getId()) {
                continue;
            }
            List<String> userTagList = gson.fromJson(tags, new TypeToken<List<String>>() {
            }.getType());
            // 计算分数 ,得到对应用户的权重
            double distance = sorce(tagList, userTagList);
            //存入
            list.add(new Pair<>(user, distance));
        }

        // 按编辑距离由小到大排序
        List<Pair<User, Double>> topUserPairList = list.stream()
                .sorted((o1, o2) -> Double.compare(o2.getValue(), o1.getValue()))
                //只排序保存前num个
                .limit(size)
                .collect(Collectors.toList());

        // 原本顺序的 userId 列表
        List<Long> userIdList = topUserPairList.stream()
                .map(pair -> pair.getKey().getId())
                .collect(Collectors.toList());
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        //查询算出来的IdList
        userQueryWrapper.in("id", userIdList);


        Map<Long, List<User>> userIdUserListMap = this.list(userQueryWrapper)
                .stream()
                //数据脱敏
                .map(user -> getSafetyUser(user))
                .collect(Collectors.groupingBy(User::getId));
        List<User> finalUserList = new ArrayList<>();
        for (Long Id : userIdList) {
            finalUserList.add(userIdUserListMap.get(Id).get(0));
        }
        return finalUserList;
    }

    /**
     * 计算权重
     * @param list1
     * @param list2
     * @return
     * @throws IOException
     */
    public double sorce(List<String> list1, List<String> list2){

        //编辑距离
        int max = Math.max(list1.size(), list2.size());
        double similarity = AllAlgorithm.minDistance(list1, list2);
        double distance = 1 - similarity / max;

        //Jacquard相似度匹配算法
        double jacquard = AllAlgorithm.calculateJacquardSimilarity(list1, list2);

        //余弦相似度
        double cosineSimilarity = AllAlgorithm.cosineSimilarity(list1, list2);

        return distance * 0.5 + jacquard * 0.3 + cosineSimilarity * 0.2;
    }

    /**
     * 用户脱敏
     *
     * @param originUser
     * @return
     */
    @Override
    public User getSafetyUser(User originUser) {
        if (originUser == null) {
            return null;
        }
        User userDto = new User();
        userDto.setId(originUser.getId());
        userDto.setUserName(originUser.getUserName());
        userDto.setUserPhone(originUser.getUserPhone());
        userDto.setUserAvatar(originUser.getUserAvatar());
        userDto.setGender(originUser.getGender());
        userDto.setUserRole(originUser.getUserRole());
        userDto.setUserTags(originUser.getUserTags());
        return userDto;
    }
}




